package com.czm.art_light.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.czm.art_light.constant.EsConsts;
import com.czm.art_light.entity.ArtUserSearchHistory;
import com.czm.art_light.mapper.ArtArtistMapper;
import com.czm.art_light.mapper.ArtWorkMapper;
import com.czm.art_light.param.request.ArtSearchReqDto;
import com.czm.art_light.param.response.*;
import com.czm.art_light.service.ArtSearchService;
import com.czm.art_light.service.ArtUserSearchHistoryService;
import com.czm.art_light.util.UserHolderUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Elasticsearch 搜索 服务实现类
 */
@Service
public class ArtSearchServiceImpl implements ArtSearchService {
    @Autowired
    private ArtUserSearchHistoryService artUserSearchHistoryService;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

/*    @Autowired
    private ArtWorkMapper artWorkMapper;

    @Autowired
    private ArtArtistMapper artArtistMapper;*/

//    根据关键字进行搜索
    /*@Override
    public ArtSearchRespDto searchByKeyword(ArtSearchReqDto artSearchReqDto) {
        //1.记录用户搜索的关键字
        artUserSearchHistoryService.addSearchHistory(ArtUserSearchHistory.builder()
                //搜索的用户ID
                .userId(UserHolderUtility.getUserId())
                //搜索的关键字
                .searchKeyword(artSearchReqDto.getKeyword())
                //搜索的时间
                .searchTime(LocalDateTime.now())
                .build());
        //2. 根据关键字对艺术作品的名称以及作家名称进行模糊查询
        // 构建分页对象
        Page<ArtSearchReqDto> page = new Page<>(artSearchReqDto.getPageNum(), artSearchReqDto.getPageSize());

        // 根据关键字对作家名称进行模糊查询
        List<ArtArtistBasicInfoRespDto> artistBasicInfoRespDtos = artArtistMapper.selectByNameLike(page, artSearchReqDto);

        //3.  根据关键字对艺术作品的名称进行模糊查询
        List<ArtWorkBasicInfoRespDto> workBasicInfoRespDtos = artWorkMapper.listArtWorkByKeyword(page, artSearchReqDto);

        return ArtSearchRespDto.builder()
                //匹配到的艺术家数据
                .artists(artistBasicInfoRespDtos)
                //匹配到的艺术作品数据
                .artworks(workBasicInfoRespDtos)
                .build();
    }*/

    /**
     * 根据关键字进行搜索
     */
    @Override
    public ArtSearchRespDto searchByKeyword(ArtSearchReqDto artSearchReqDto) {
        // 1. 记录用户搜索的关键字
        artUserSearchHistoryService.addSearchHistory(ArtUserSearchHistory.builder()
                // 搜索的用户ID
                .userId(UserHolderUtility.getUserId())
                // 搜索的关键字
                .searchKeyword(artSearchReqDto.getKeyword())
                // 搜索的时间
                .searchTime(LocalDateTime.now()).build());

        // 2. 使用 Elasticsearch 进行搜索
        List<ArtArtistBasicInfoRespDto> artistBasicInfoRespDtos = searchArtistsByKeyword(artSearchReqDto);
        List<ArtWorkBasicInfoRespDto> workBasicInfoRespDtos = searchArtworksByKeyword(artSearchReqDto);

        return ArtSearchRespDto.builder().artists(artistBasicInfoRespDtos) // 匹配到的艺术家数据
                .artworks(workBasicInfoRespDtos) // 匹配到的艺术作品数据
                .build();
    }

    /**
     * 根据关键字搜索艺术家
     */
    private List<ArtArtistBasicInfoRespDto> searchArtistsByKeyword(ArtSearchReqDto artSearchReqDto) {
        try {
            // 构建 Elasticsearch 查询
            SearchResponse<ArtArtistEsRespDto> response = elasticsearchClient.search(s -> s
                    // 指定索引
                    .index(EsConsts.ArtistIndex.INDEX_NAME)
                    .query(q -> q
                            .scriptScore(ss -> ss
                                    // 基础查询：匹配关键字
                                    .query(q2 -> q2
                                            .multiMatch(m -> m
                                                    // 搜索关键字
                                                    .query(artSearchReqDto.getKeyword())
                                                    // 搜索字段 支持全文+精确匹配
                                                    .fields(
                                                            EsConsts.ArtistIndex.FIELD_ARTIST_NAME,
                                                            EsConsts.ArtistIndex.FIELD_ARTIST_NAME + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtistIndex.FIELD_ALL_TOPIC_NAMES,
                                                            EsConsts.ArtistIndex.FIELD_ALL_TOPIC_NAMES + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtistIndex.FIELD_ALL_CATEGORY_NAMES,
                                                            EsConsts.ArtistIndex.FIELD_ALL_CATEGORY_NAMES + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtistIndex.FIELD_ALL_MOVEMENT_NAMES,
                                                            EsConsts.ArtistIndex.FIELD_ALL_MOVEMENT_NAMES + EsConsts.EXACT_MATCH_KEYWORD
                                                    )
                                                    .operator(Operator.Or)
                                            )
                                    )
                                    // 自定义评分脚本
                                    .script(sc -> sc
                                            .inline(i -> i
                                                    .source(EsConsts.CUSTOM_ARTIST_SCORE_SCRIPT)
                                            )
                                    )
                            )
                    )
                    // 分页起始位置
                    .from((artSearchReqDto.getPageNum() - 1) * artSearchReqDto.getPageSize())
                    // 分页大小
                    .size(artSearchReqDto.getPageSize()), ArtArtistEsRespDto.class);

            // 提取搜索结果
            TotalHits total = response.hits().total();
            List<ArtArtistBasicInfoRespDto> list = new ArrayList<>();
            List<Hit<ArtArtistEsRespDto>> hits = response.hits().hits();
            // 类型推断 var 非常适合 for 循环，JDK 10 引入，JDK 11 改进
            for (var hit : hits) {
                ArtArtistEsRespDto artArtistEsRespDto = hit.source();
                assert artArtistEsRespDto != null;
                list.add(ArtArtistBasicInfoRespDto.builder()
                        // 艺术家id
                        .id(artArtistEsRespDto.getId())
                        // 艺术家名字
                        .artistName(artArtistEsRespDto.getArtistName())
                        // 艺术家照片的URL地址
                        .photoUrl(artArtistEsRespDto.getPhotoUrl())
                        // 艺术家国籍
                        .nationality(artArtistEsRespDto.getNationality())
                        // 艺术家的出生和逝世年份(已经逝世的情况下)，格式为YYYY-YYYY
                        .birthDeathYears(artArtistEsRespDto.getBirthDeathYears())
                        // 总的艺术画作品数量
                        .totalArtworkCount(artArtistEsRespDto.getTotalArtworkCount())
                        .build());
            }
            assert total != null;
            return list;
        } catch (IOException e) {
            return Collections.emptyList();
        }
    }

    /**
     * 根据关键字搜索艺术作品
     */
    private List<ArtWorkBasicInfoRespDto> searchArtworksByKeyword(ArtSearchReqDto artSearchReqDto) {
        try {
            // 构建 Elasticsearch 查询
            SearchResponse<ArtWorkEsRespDto> response = elasticsearchClient.search(s -> s
                    // 指定索引
                    .index(EsConsts.ArtWorkIndex.INDEX_NAME)
                    .query(q -> q
                            .scriptScore(ss -> ss
                                    // 基础查询：匹配关键字
                                    .query(q2 -> q2
                                            .multiMatch(m -> m
                                                    // 搜索关键字
                                                    .query(artSearchReqDto.getKeyword())
                                                    // 搜索字段 支持全文+精确匹配
                                                    .fields(
                                                            EsConsts.ArtWorkIndex.FIELD_TITLE,
                                                            EsConsts.ArtWorkIndex.FIELD_TITLE + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtWorkIndex.FIELD_ARTIST_NAME,
                                                            EsConsts.ArtWorkIndex.FIELD_ARTIST_NAME + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtWorkIndex.FIELD_TOPIC_NAME,
                                                            EsConsts.ArtWorkIndex.FIELD_TOPIC_NAME + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtWorkIndex.FIELD_CATEGORY_NAME,
                                                            EsConsts.ArtWorkIndex.FIELD_CATEGORY_NAME + EsConsts.EXACT_MATCH_KEYWORD,
                                                            EsConsts.ArtWorkIndex.FIELD_MOVEMENT_NAME,
                                                            EsConsts.ArtWorkIndex.FIELD_MOVEMENT_NAME + EsConsts.EXACT_MATCH_KEYWORD
                                                    )
                                                    .operator(Operator.Or)
                                            )
                                    )
                                    // 自定义评分脚本
                                    .script(sc -> sc
                                            .inline(i -> i
                                                    .source(EsConsts.CUSTOM_ART_WORKS_SCORE_SCRIPT)
                                            )
                                    )
                            )
                    )
                    // 分页起始位置
                    .from((artSearchReqDto.getPageNum() - 1) * artSearchReqDto.getPageSize())
                    // 分页大小
                    .size(artSearchReqDto.getPageSize()), ArtWorkEsRespDto.class);

            // 提取搜索结果
            TotalHits total = response.hits().total();
            List<ArtWorkBasicInfoRespDto> list = new ArrayList<>();
            List<Hit<ArtWorkEsRespDto>> hits = response.hits().hits();
            // 类型推断 var 非常适合 for 循环，JDK 10 引入，JDK 11 改进
            for (var hit : hits) {
                ArtWorkEsRespDto artWorkEsRespDto = hit.source();
                assert artWorkEsRespDto != null;
                list.add(ArtWorkBasicInfoRespDto.builder()
                        // 作品id
                        .id(artWorkEsRespDto.getId())
                        // 作品名称
                        .title(artWorkEsRespDto.getTitle())
                        // 艺术家名字
                        .artistName(artWorkEsRespDto.getArtistName())
                        // 作品主图小图
                        .primaryImageSmallUrl(artWorkEsRespDto.getPrimaryImageSmallUrl())
                        .build());
            }
            assert total != null;
            return list;
        } catch (IOException e) {
            return Collections.emptyList();
        }
    }
}
